package com.ocm.mapper;

import java.util.ArrayList;
import java.util.List;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import net.sf.json.util.PropertyFilter;

public class CustomPropertyFilter implements PropertyFilter {

   private String pathChildObject;

   private List<Object> propertySource;

   private String[] excludeProperties;

   private List<String> excludePropertiesList;

   public CustomPropertyFilter() {
      pathChildObject="";
      propertySource=new ArrayList<Object>(); 
   }

   public String[] getExcludeProperties() {
      return excludeProperties;
   }

   public void setExcludeProperties(String[] excludeProperties) {
      this.excludeProperties = excludeProperties;
   }

   public boolean apply(Object source, String name, Object value) {
      if (propertySource.size()==0){
         propertySource.add(source);
         assignNameToExcludeProperties(source);
      }
      processLatestPath(source, name, value);
      String propertyName = this.pathChildObject;
      if (this.excludePropertiesList.contains(propertyName)){
         return true;
      }
      return false;
   }

   public void reset(){
      pathChildObject="";
      propertySource=new ArrayList<Object>();
      excludePropertiesList=new ArrayList<String>();
   }

   @SuppressWarnings("unchecked")
   private void processLatestPath(Object source, String name, Object value){
      if (source instanceof JSONObject || source instanceof JSONArray){
         pathChildObject=pathChildObject.substring(0, pathChildObject.lastIndexOf("."));
         pathChildObject=pathChildObject.concat("."+name);
         return;
      }
      boolean hasSameLatestSource=false;
      Object latest=propertySource.get(propertySource.size()-1);
      if (latest != null && latest.getClass().getName().equals(source.getClass().getName()) && latest.equals(source))
         hasSameLatestSource=true;

      int latestIndex=-1;
      if (hasSameLatestSource==false){
         for (int index=0;index<propertySource.size()-1;index++){
            latest=propertySource.get(index);
            if (latest.getClass().getName().equals(source.getClass().getName()) && latest.equals(source)){
               latestIndex = index;
               break;
            }
         }
      }
      if(propertySource.size()>1){
         if (!hasSameLatestSource){
            if (latestIndex >= 0){
               int index = latestIndex+1;
               while (index<=propertySource.size()){
                  pathChildObject=pathChildObject.substring(0, pathChildObject.lastIndexOf("."));
                  propertySource.remove(propertySource.size()-1);
                  index++;
               }
            }else{
               pathChildObject=pathChildObject.substring(0, pathChildObject.lastIndexOf("."));
               propertySource.remove(propertySource.size()-1);
            }
         }
         pathChildObject=pathChildObject.concat(".");
      }
      pathChildObject=pathChildObject.concat(name);
      Object expectedValue = value;
      if (hasList(value)){
         List list = (List) value; 
         if (list.size() > 0){
            expectedValue= list.get(0);
         }else{
            expectedValue=null;
         }
      }
      propertySource.add(propertySource.size(), expectedValue);
   }
   
   @SuppressWarnings("unchecked")
   private boolean hasList(Object value){
      boolean result=false;
      if (value != null && value instanceof List){
         result = true;
      }
      return result;
   }
   
   private void assignNameToExcludeProperties(Object source){
      this.excludePropertiesList=new ArrayList<String>();
      if (this.excludeProperties != null){
         String simpleName=source.getClass().getSimpleName();
         for (String excludeProperty:excludeProperties){
            this.excludePropertiesList.add(this.excludePropertiesList.size(),simpleName.concat("."+excludeProperty));
         }
         pathChildObject=pathChildObject.concat(simpleName+".");
      }
   }
}
